import axios, {
  AxiosInstance,
  AxiosRequestConfig,
  Method,
  ResponseType,
} from "axios";
import { ElMessage } from "element-plus";
import useStore from "../store/index";
/** http请求类，集成拦截器处理
 * opts = {
 *     baseURL: string  基础URL地址
 *     timeout: number  超时时长（毫秒）
 *     token: string    每次请求携带的Authorization值
 *     requestHandle function(config):config 请求处理函数，返回处理后的config
 *     responseHandle function(response):response 响应处理函数，返回处理后的response
 * }
 */
interface PendingType {
  url: string;
  method?: Method;
  data?: any;
  responseType?: ResponseType;
  isToken?: boolean;
}
interface Params {
  routeParam?: any;
  queryParam?: any;
}
class Http {
  http: AxiosInstance;
  isToken?: boolean = true;
  constructor() {
    this.http = axios.create({
      baseURL: process.env.VUE_APP_API_URL,
      timeout: 5000,
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
      },
    });

    const pending: any = {};
    const CancelToken = axios.CancelToken;
    const removePending = (key: any, isRequest = false) => {
      if (pending[key] && isRequest) {
        pending[key]("请勿重复请求");
        ElMessage({ message: "请勿重复请求，请稍等！！", type: "warning" });
      }
      delete pending[key];
    };
    const getRequestIdentify = (config: any, isRequest = false) => {
      let url = config.url;
      if (isRequest) {
        url = config.baseURL + config.url.substring(1, config.url.length);
      }
      const arr = ["put", "post"];
      return arr.includes(config.method)
        ? encodeURIComponent(url + JSON.stringify(config.params))
        : encodeURIComponent(config.url + JSON.stringify(config.data));
    };
    // 添加请求拦截器
    this.http.interceptors.request.use(
      (config: AxiosRequestConfig) => {
        // 发送请求之前，拦截重复请求(即当前正在进行的相同请求)
        const requestData = getRequestIdentify(config, true)
        removePending(requestData, true)
        config.cancelToken = new CancelToken((c) => {
          pending[requestData] = c
        })
        const token = useStore.getters.getToken;
        if (config && config?.headers) {
          config.headers["Content-Type"] = "application/x-www-form-urlencoded";
        }

        // 在发送请求之前做些什么
        config.params = {
          token,
        };
        return config;
      },
      function (error) {
        // 对请求错误做些什么
        return Promise.reject(error);
      }
    );

    // 添加响应拦截器
    this.http.interceptors.response.use(
      (response) => {
        if (
          response.status === 200 &&
          response.request.responseType === "blob"
        ) {
          return response.data;
        }
        if (response.status === 200 && response.data.code === 0) {
          if (response.data.data) {
            return response.data.data;
          }
          return response.data;
        }
        if (response.status === 200 && response.data.code === 2000) {
          useStore.commit("OUT_LOGIN");
          // router.push({ path: "/login" });
        }
        ElMessage({ message: response.data.msg, type: "error" });
        return Promise.reject(response.data);
      },
      (error) => {
        // 对响应错误做点什么
        return error;
      }
    );
  }
  /**
   * 解析URL
   * @param url <string> '/aaa/:id'
   * @param routeParam = <object|map> {id: 1234} 将使用此对象的属性值替换url中的参数
   * @param queryParam = <object|map> {name: 'abc', attr: 'def'} url后面的查询参数
   * @returns {string}
   */

  parseUrl(url: string, params: Params) {
    if (params.routeParam) {
      url = url.replace(/:([^/]+)/g, (match, key) => {
        return params.routeParam[key];
      });
    }
    if (params.queryParam) {
      const queryStrings = [];
      for (const i in params.queryParam) {
        queryStrings.push(`${i}=${params.queryParam[i]}`);
      }
      url += "?" + queryStrings.join("&");
    }
    return url;
  }
  /**
   * 请求方法
   * @param url <string> '/aaa/:id'
   * @param param <object|map> {
   *     routeParam = {id: 1234} 将使用此对象的属性值替换url中的参数
   *     queryParam = {name: 'abc', attr: 'def'} url后面的查询参数
   * }
   * 解析后的 url = '/aaa/1234?name=abc&attr=def'
   * @returns {Promise<*>}
   */
  get(types: PendingType, params?: Params) {
    if (params?.routeParam || params?.queryParam) {
      types.url = this.parseUrl(types.url, params);
    }
    this.isToken = types.isToken;
    return this.http.get(types.url, { responseType: types.responseType });
  }
  post(types: PendingType, params?: Params) {
    if (params?.routeParam || params?.queryParam) {
      types.url = this.parseUrl(types.url, params);
    }
    this.isToken = types.isToken;
    return this.http.post(types.url, types.data, {
      responseType: types.responseType,
    });
  }
  put(types: PendingType, params?: Params) {
    if (params?.routeParam || params?.queryParam) {
      types.url = this.parseUrl(types.url, params);
    }
    this.isToken = types.isToken;
    return this.http.put(types.url, types.data, {
      responseType: types.responseType,
    });
  }
  patch(types: PendingType, params?: Params) {
    if (params?.routeParam || params?.queryParam) {
      types.url = this.parseUrl(types.url, params);
    }
    this.isToken = types.isToken;
    return this.http.patch(types.url, types.data, {
      responseType: types.responseType,
    });
  }
  delete(types: PendingType, params?: Params) {
    if (params?.routeParam || params?.queryParam) {
      types.url = this.parseUrl(types.url, params);
    }
    this.isToken = types.isToken;
    return this.http.delete(types.url, { responseType: types.responseType });
  }
}

export const api = new Http();
